/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Global Trust Authority is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use std::sync::Arc;

use actix_web::web::{Json, Query};
use actix_web::{web, HttpRequest, HttpResponse};
use log::{error, info};
use sea_orm::DatabaseConnection;
use serde_json::{Map, Value};
use uuid::Uuid;

use crate::entities::inner_model::rv_model::RefValueModelBuilder;
use crate::entities::request_body::rv_add_req_body::RvAddReqBody;
use crate::entities::request_body::rv_del_req_body::RvDelReqBody;
use crate::entities::request_body::rv_update_req_body::RvUpdateReqBody;
use crate::error::ref_value_error::RefValueError;
use crate::repositories::rv_db_repo::RvDbRepo;
use crate::services::rv_factory::RvFactory;
use crate::services::rv_trait::RefValueTrait;
use crate::utils::utils::Utils;

/// Adds a new reference value to the system.
///
/// # Parameters
/// * `req` - The HTTP request containing authentication and context information
/// * `db` - Database connection wrapped in web::Data
/// * `req_body` - JSON payload containing the reference value data
///
/// # Returns
/// * `HttpResponse` - Response indicating success or failure of the operation
pub async fn add_ref_value(
    req: HttpRequest,
    db: web::Data<Arc<DatabaseConnection>>,
    req_body: Json<Value>,
) -> Result<HttpResponse, RefValueError> {
        let user_id = Utils::extract_user_id(&req)?;
        let add_rv_body = Utils::parse_and_validate::<RvAddReqBody>(req_body)?;
        Utils::verify_by_cert(&user_id, &add_rv_body.content).await?;

        let id = Uuid::new_v4().to_string();

        let mut rv_model = RefValueModelBuilder::new()
            .id(&id)
            .uid(&user_id)
            .name(&add_rv_body.name)
            .op_description(&add_rv_body.description)
            .attester_type(&add_rv_body.attester_type)
            .content(&add_rv_body.content)
            .build();

        if Utils::is_require_sign()? {
            let (signature, key_version) = Utils::sign_by_ref_value_model(&rv_model).await?;
            rv_model.set_signature(&signature);
            rv_model.set_key_version(&key_version);
        }

        let implementation = RvFactory::create_ref_value();
        implementation.add(db, &rv_model).await?;

        info!("Ref value added successfully");
        Ok(HttpResponse::Ok().json(serde_json::json!({
            "ref_value": {
                "id": rv_model.id,
                "name": add_rv_body.name,
                "version": 1,
            }
        })))
    }

/// Updates an existing reference value in the system.
///
/// # Parameters
/// * `req` - The HTTP request containing authentication and context information
/// * `db` - Database connection wrapped in web::Data
/// * `req_body` - JSON payload containing the updated reference value data
///
/// # Returns
/// * `HttpResponse` - Response indicating success or failure of the operation
pub async fn update_ref_value(
    req: HttpRequest,
    db: web::Data<Arc<DatabaseConnection>>,
    req_body: Json<Value>,
) -> Result<HttpResponse, RefValueError> {
        let user_id = Utils::extract_user_id(&req)?;
        let update_rv_body = Utils::parse_and_validate::<RvUpdateReqBody>(req_body)?;

        if let Some(ref content) = update_rv_body.content {
            Utils::verify_by_cert(&user_id, content).await?;
        }

        let implementation = RvFactory::create_ref_value();
        let updated_model = implementation.update(db, &user_id, &update_rv_body).await?;

        info!("Reference Value updated successfully");
        Ok(HttpResponse::Ok().json(serde_json::json!({
            "ref_value": {
                "id": updated_model.id,
                "name": updated_model.name,
                "version": updated_model.version,
            }
        })))
    }

/// Deletes an existing reference value from the system.
///
/// # Parameters
/// * `req` - The HTTP request containing authentication and context information
/// * `db` - Database connection wrapped in web::Data
/// * `req_body` - JSON payload containing the reference value identifier
///
/// # Returns
/// * `HttpResponse` - Response indicating success or failure of the operation
pub async fn delete_ref_value(
    req: HttpRequest,
    db: web::Data<Arc<DatabaseConnection>>,
    req_body: Json<Value>,
) -> Result<HttpResponse, RefValueError> {
        let user_id = Utils::extract_user_id(&req)?;
        let rv_del_req_body = Utils::parse_and_validate::<RvDelReqBody>(req_body)?;
        let delete_type = rv_del_req_body.delete_type.as_str();

        let implementation = RvFactory::create_ref_value();
        implementation
            .delete(db, &user_id, &delete_type, &rv_del_req_body)
            .await
            .map(|_| {
                info!("Reference Value deleted successfully");
                HttpResponse::Ok().finish()
            })
            .map_err(|e| {
                error!("Failed to delete Reference Value: {:?}", e.to_string());
                e
            })
    }

/// Queries reference value based on provided parameters.
///
/// # Parameters
/// * `req` - The HTTP request containing authentication and query parameters
/// * `db` - Database connection wrapped in web::Data
///
/// # Returns
/// * `HttpResponse` - Response containing the query results or error message
pub async fn query_ref_value(
        req: HttpRequest,
        db: web::Data<Arc<DatabaseConnection>>,
    ) -> Result<HttpResponse, RefValueError> {
        let user_id = Utils::extract_user_id(&req)?;
        let query_params = Query::<Value>::from_query(req.query_string())
            .map_err(|e| RefValueError::InvalidParameter(format!("Invalid query parameters: {}", e)))?;

        if let Some(ids) = Utils::extract_ids_param(&query_params)? {
            return Utils::handle_response(RvDbRepo::query_by_ids(&db, &user_id, &ids).await);
        }

        let res = if let Some(attester_type) = Utils::extract_attester_type(&query_params)? {
            RvDbRepo::query_all_by_attester_type(&db, &user_id, attester_type).await
        } else {
            RvDbRepo::query_all(&db, &user_id).await
        };

        let standard_res = res.map(|vec_model| {
            let response_list: Vec<Map<String, Value>> = vec_model
                .iter()
                .map(|m| {
                    let mut response = serde_json::Map::new();
                    response.insert("id".to_string(), Value::String(m.id.to_string()));
                    response.insert("name".to_string(), Value::String(m.name.to_string()));
                    response.insert(
                        "attester_type".to_string(),
                        Value::String(m.attester_type.to_string()),
                    );
                    response
                })
                .collect();
            response_list
        });

        Utils::handle_response(standard_res)
}

/// Verifies measurements against reference values.
///
/// # Parameters
/// * `measurements` - The measurements to verify
/// * `user_id` - The user ID for the verification context
/// * `attester_type` - The type of attester
///
/// # Returns
/// * `Result<Vec<String>, String>` - Verification results or error message
pub async fn verify_measurements(
    measurements: &Vec<String>,
    user_id: &str,
    attester_type: &str,
) -> Result<Vec<String>, String> {
    let implementation = RvFactory::create_ref_value();
    implementation.verify(measurements, user_id, attester_type).await
}